fix: add managemement command to filter hook - #200
Conversation
There was a problem hiding this comment.
Pull request overview
Adds an Open edX Filters integration point to manage.py so Django management command execution can be intercepted (e.g., wrapped/monitored) via configured filter pipelines, keeping org-specific logic out of edx-platform.
Changes:
- Introduces a new public filter type (
org.openedx.platform.management.command.execute.requested.v1) for management command execution. - Wraps
execute_from_command_linein acommand_runnercallable and passes it through the filter pipeline before execution.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 1 out of 1 changed files in this pull request and generated no new comments.
Comments suppressed due to low confidence (1)
manage.py:135
OpenEdxPublicFilter.run_pipeline()appears to return a tuple of the filtered values (as used elsewhere in this repo via tuple-unpacking of*.run_filter(...)). Treating the result as a dict (pipeline_output.get(...)) will raiseAttributeErrorand cause the broadexceptto always fall back tocommand_runner, effectively preventing the filter pipeline from ever overriding the runner.
pipeline_output = ManagementCommandExecutionRequested.run_filter(
command_name=command_name,
service_variant=os.environ.get("SERVICE_VARIANT", edx_args.service_variant),
command_runner=command_runner,
)
| def command_runner(): | ||
| return execute_from_command_line([sys.argv[0]] + django_args) | ||
|
|
||
| command_name = next((arg for arg in django_args if not arg.startswith('-')), 'help') |
There was a problem hiding this comment.
IMO, this method of calculating the command name is obscure and confusing. At least add a code comment explaining the rationale, and why the default value of "help" makes sense. Give a realistic example value of django_args and what the resulting output is supposed to be for that input value.
There was a problem hiding this comment.
I added an explicit comment explaining how command_name is derived (first non-option token from django_args), why the fallback is help, and a concrete example input/output (for example: --verbosity 2 migrate --noinput -> migrate; --help -> help). This should make the intent clearer for future readers.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 1 out of 1 changed files in this pull request and generated no new comments.
Comments suppressed due to low confidence (3)
manage.py:138
- The current command name extraction will incorrectly pick option values as the command. For example, with
django_args=['--verbosity', '2', 'migrate', '--noinput']this code returns'2', not'migrate', because it selects the first token that doesn't start with '-'. This will cause the filter pipeline to receive the wrongcommand_namefor common Django global options that take values.
# We treat the first non-option token as the Django command name.
# Example: django_args=['--verbosity', '2', 'migrate', '--noinput'] -> 'migrate'.
# If there is no non-option token (for example, django_args=['--help']),
# default to 'help' because Django will print command help in that case.
command_name = next((arg for arg in django_args if not arg.startswith('-')), 'help')
manage.py:30
- Class name uses
Contextmanagerrather than the standardContextManagerCamelCase, which makes the name harder to read and inconsistent with typical Python naming for context managers.
class ManagementCommandContextmanagerRequested(OpenEdxPublicFilter):
manage.py:140
- If the class is renamed to
ManagementCommandContextManagerRequested, the call site also needs to be updated to match; otherwise this will raise aNameErrorat runtime.
command_contextmanager = ManagementCommandContextmanagerRequested.run_filter(
| command_contextmanager = ManagementCommandContextmanagerRequested.run_filter( | ||
| command_contextmanager=nullcontext(), | ||
| command_name=command_name, | ||
| service_variant=os.environ.get("SERVICE_VARIANT", edx_args.service_variant), | ||
| ) |
There was a problem hiding this comment.
Output signature should match input signature:
| command_contextmanager = ManagementCommandContextmanagerRequested.run_filter( | |
| command_contextmanager=nullcontext(), | |
| command_name=command_name, | |
| service_variant=os.environ.get("SERVICE_VARIANT", edx_args.service_variant), | |
| ) | |
| command_contextmanager, _, _ = ManagementCommandContextmanagerRequested.run_filter( | |
| command_contextmanager=nullcontext(), | |
| command_name=command_name, | |
| service_variant=os.environ.get("SERVICE_VARIANT", edx_args.service_variant), | |
| ) |
| from openedx_filters.tooling import OpenEdxPublicFilter | ||
|
|
||
|
|
||
| class ManagementCommandContextmanagerRequested(OpenEdxPublicFilter): |
There was a problem hiding this comment.
This class is going to be deleted, right? It's just here temporarily to make the tests pass?
There was a problem hiding this comment.
Yes, this is only a temporary workaround to get the tests passing. Once openedx-filters PR is merged and released, we'll update the openedx-filters dependency in edx-platform's requirements and remove the fallback class definition from manage.py.
|
I also just wanted to make sure you plan to add this filter call upstream to openedx/openedx-platform |
| # django_args contains only args that argparse did not consume. | ||
| # Django treats the first positional token as the command name. | ||
| # Example: django_args=['migrate', '--noinput'] -> 'migrate'. | ||
| # If the first token is an option (for example, django_args=['--help']), | ||
| # default to 'help' so the filter sees a command-like label. | ||
| command_name = django_args[0] if django_args and not django_args[0].startswith('-') else 'help' |
There was a problem hiding this comment.
Would --help be an illegal name for command_name? Why not just make this django_args[0], with maybe a default of no-arg-supplied if there are no arguments?
There was a problem hiding this comment.
+1, it does feel like falling back to help can be misleading.
There was a problem hiding this comment.
@robrap @pwnage101 The command_name extraction in manage.py now uses django_args[0] directly, allowing option flags such as --help to be passed through unchanged instead of being mapped to help. If no arguments are provided, it defaults to no-argument.
@pwnage101 Yes, I'm going to create an upstream PR. Once this PR is approved, I'll close it, open a new PR upstream, get that merged, and then cherry-pick the changes back into this Fork. |
Description
Add a pluggable filter hook in manage.py so Django management command execution can be intercepted by configured Open edX filter pipelines. This keeps edx-platform limited to the public integration point and avoids embedding org-specific monitoring logic here
Reference Ticket
https://2u-internal.atlassian.net/browse/BOMS-151